`include "ysyx_23060189_isa.svh"
`include "ysyx_23060189_cpu.svh"
`include "ysyx_23060189_decode.svh"

module ysyx_23060189_Mem (
  input  wire                            ACLK,
  input  wire                            ARESETn,

  /* Mem <=> Master interface */
  // read
  output reg                             ren,
  output wire [`ysyx_23060189_AddrBus]   raddr,
  output wire [2:0]                      arsize,
  input  wire [`ysyx_23060189_DataBus]   rdata,
  input  wire                            rvalid,

  // write
  output reg                             wen,
  output wire [`ysyx_23060189_AddrBus]   waddr,
  output wire [`ysyx_23060189_DataBus]   wdata,
  output wire [7:0]                      wmask,
  output wire [2:0]                      awsize,
  input  wire                            wdone,

  /* MEU <=> Mem */
  input  wire                            meu_valid,
  input  wire [`ysyx_23060189_AddrBus]   addr,
  input  wire [`ysyx_23060189_StTypeBus] st_type,
  input  wire [`ysyx_23060189_LdTypeBus] ld_type,
  input  wire [`ysyx_23060189_DataBus]   wr_data,
  output wire [`ysyx_23060189_DataBus]   rd_data,
  output wire                            mem_valid
);
  // wire define
  wire st_valid = st_type[1] | st_type[0];
  wire ld_valid = ld_type[2] | ld_type[1] | ld_type[0];

  // wire valid = st_valid | ld_valid;

  assign raddr  = addr;
  assign arsize = ld_type == `ysyx_23060189_LD_LB  ? 3'd0 :
                  ld_type == `ysyx_23060189_LD_LBU ? 3'd0 :
                  ld_type == `ysyx_23060189_LD_LH  ? 3'd1 :
                  ld_type == `ysyx_23060189_LD_LHU ? 3'd1 :
                  ld_type == `ysyx_23060189_LD_LW  ? 3'd2 : 3'd2;

  assign waddr  = addr;
  assign wdata  = wr_data;
  assign awsize = st_type == `ysyx_23060189_ST_SB ? 3'd0 :
                  st_type == `ysyx_23060189_ST_SH ? 3'd1 :
                  st_type == `ysyx_23060189_ST_SW ? 3'd2 : 3'd2;

  assign mem_valid = st_valid | ld_valid ? (rvalid | wdone) : 1;

  // MuxKey #(4, `ysyx_23060189_ST_TYPE_W, 1) Mux_store (st_valid, st_type, {
  //     `ysyx_23060189_ST_SB,  1'b1,
  //     `ysyx_23060189_ST_SH,  1'b1,
  //     `ysyx_23060189_ST_SW,  1'b1,
  //     `ysyx_23060189_ST_XXX, 1'b0
  // });
  //
  // MuxKey #(6, `ysyx_23060189_LD_TYPE_W, 1) Mux_load (ld_valid, ld_type, {
  //     `ysyx_23060189_LD_LB,  1'b1,
  //     `ysyx_23060189_LD_LH,  1'b1,
  //     `ysyx_23060189_LD_LW,  1'b1,
  //     `ysyx_23060189_LD_LBU, 1'b1,
  //     `ysyx_23060189_LD_LHU, 1'b1,
  //     `ysyx_23060189_LD_XXX, 1'b0
  // });

  MuxKey #(13, 2 + `ysyx_23060189_LD_TYPE_W, `ysyx_23060189_DATA_W) Mux_rd_data (rd_data, {addr[1:0], ld_type}, {
    {2'b00, `ysyx_23060189_LD_LB}, {{24{rdata[7]}}, rdata[7:0]},
    {2'b01, `ysyx_23060189_LD_LB}, {{24{rdata[15]}}, rdata[15:8]},
    {2'b10, `ysyx_23060189_LD_LB}, {{24{rdata[23]}}, rdata[23:16]},
    {2'b11, `ysyx_23060189_LD_LB}, {{24{rdata[31]}}, rdata[31:24]},
    {2'b00, `ysyx_23060189_LD_LH}, {{16{rdata[15]}}, rdata[15:0]},
    {2'b10, `ysyx_23060189_LD_LH}, {{16{rdata[31]}}, rdata[31:16]},
    {2'b00, `ysyx_23060189_LD_LW},  rdata,
    {2'b00, `ysyx_23060189_LD_LBU}, {{24'b0}, rdata[7:0]},
    {2'b01, `ysyx_23060189_LD_LBU}, {{24'b0}, rdata[15:8]},
    {2'b10, `ysyx_23060189_LD_LBU}, {{24'b0}, rdata[23:16]},
    {2'b11, `ysyx_23060189_LD_LBU}, {{24'b0}, rdata[31:24]},
    {2'b00, `ysyx_23060189_LD_LHU}, {{16'b0}, rdata[15:0]},
    {2'b10, `ysyx_23060189_LD_LHU}, {{16'b0}, rdata[31:16]}
  });

  MuxKey #(7, 2 + `ysyx_23060189_ST_TYPE_W, 8) Mux_wmask (wmask, {addr[1:0], st_type}, {
    {2'b00, `ysyx_23060189_ST_SB}, 8'b00000001,
    {2'b01, `ysyx_23060189_ST_SB}, 8'b00000010,
    {2'b10, `ysyx_23060189_ST_SB}, 8'b00000100,
    {2'b11, `ysyx_23060189_ST_SB}, 8'b00001000,
    {2'b00, `ysyx_23060189_ST_SH}, 8'b00000011,
    {2'b10, `ysyx_23060189_ST_SH}, 8'b00001100,
    {2'b00, `ysyx_23060189_ST_SW}, 8'b00001111
  });

  MuxKey #(7, 2 + `ysyx_23060189_ST_TYPE_W, `ysyx_23060189_DATA_W) Mux_wdata (wdata, {addr[1:0], st_type}, {
    {2'b00, `ysyx_23060189_ST_SB}, wr_data,
    {2'b01, `ysyx_23060189_ST_SB}, wr_data << 8,
    {2'b10, `ysyx_23060189_ST_SB}, wr_data << 16,
    {2'b11, `ysyx_23060189_ST_SB}, wr_data << 24,
    {2'b00, `ysyx_23060189_ST_SH}, wr_data,
    {2'b10, `ysyx_23060189_ST_SH}, wr_data << 16,
    {2'b00, `ysyx_23060189_ST_SW}, wr_data
  });

  localparam IDEL  = 4'b0001;
  localparam READ  = 4'b0010;
  localparam WRITE = 4'b0100;
  localparam WAIT  = 4'b1000;

  reg [3:0] curr_status;
  reg [3:0] next_status;

  // status machine
  always @(posedge ACLK) begin
    if (ARESETn == 0) curr_status <= IDEL;
    else curr_status <= next_status;
  end

  always @(*) begin
    case (curr_status)
      IDEL :
        if (meu_valid && ld_valid) next_status = READ;
        else if (meu_valid && st_valid) next_status = WRITE;
        else next_status = IDEL;

      READ :
        next_status = WAIT;

      WRITE:
        next_status = WAIT;

      WAIT :
        if (meu_valid == 0) next_status = IDEL;
        else next_status = WAIT;

      default:
        next_status = IDEL;
    endcase
  end

  // ren
  always @(posedge ACLK) begin
    if (ARESETn == 0) ren <= 1'b0;
    else if (curr_status == READ) ren <= 1'b1;
    else ren <= 0;
  end

  // wen
  always @(posedge ACLK) begin
    if (ARESETn == 0) wen <= 1'b0;
    else if (curr_status == WRITE) wen <= 1'b1;
    else wen <= 0;
  end

endmodule
